home *** CD-ROM | disk | FTP | other *** search
/ Pascal Super Library / Pascal Super Library (CW International)(1997).bin / DELPHI32 / COMM / QCCOM32 / QCCOM32.ZIP / QCCom32.pas < prev   
Pascal/Delphi Source File  |  1996-05-03  |  11KB  |  465 lines

  1. unit QCCom32;
  2. (*
  3. (c) 1996 Quantum Composers, Bozeman, MT
  4. This file contains Delphi source code to a Delphi Control used for
  5. serial port communications. 32-bit version.
  6. Author: Scott Pinkham
  7. Date Created: 2/9/96
  8. *)
  9. interface
  10.  
  11. uses
  12.     Windows,
  13.   Classes,
  14.   ExtCtrls,
  15.   SysUtils,
  16.   Forms,
  17.   Dialogs,
  18.   Graphics,
  19.   Controls,
  20.   Buttons,
  21.   StdCtrls;
  22.  
  23. { Class Definition ------------------------------}
  24.  
  25. { dialog box to pick a COM port }
  26. type
  27.     TformPickCom = class(TForm)
  28.         OKBtn: TBitBtn;
  29.         CancelBtn: TBitBtn;
  30.         radioCom: TRadioGroup;
  31.  
  32.     private
  33.       { Private declarations }
  34.     public
  35.       { Public declarations }
  36. end;
  37.  
  38. { ComPort class }
  39. type
  40.     T_QCCom32 = class(TComponent)
  41.  
  42.     private  { Private declarations }
  43.         hPort: LongInt; {handle from OpenComm }
  44.         nPort: Integer; { port #, 1-based }
  45.         lBaud: LongInt; { baud rate }
  46.         sInTerminator: String; { response terminator string }
  47.         boolExpired: Boolean; { set true when a timeout occurs }
  48.         Timer: TTimer; {timeout timer }
  49.         formPickCom: TformPickCom; { dialog box to pick a port }
  50.         boolShowErrors: Boolean; { true to enable error boxes }
  51.         boolInUse: Boolean; { true while port is blocked, waiting for response }
  52.  
  53.         { Events (method pointers) user can assign code to these }
  54.         pmOnTimeout: TNotifyEvent;    { timeout event }
  55.  
  56.         { functions }
  57.         procedure SetTimeout(lTimeout: LongInt);
  58.         function GetTimeout: LongInt;
  59.         procedure SetBaud(lBaudToSet: LongInt);
  60.         procedure SetPort(nPortToSet: Integer);
  61.         function GetInCount: LongInt;
  62.         procedure TimesUp(Sender: TObject);
  63.         function IsOpen: Boolean;
  64.  
  65.         { dialog box button handlers }
  66.         //procedure FormActivate(Sender: TObject);
  67.         //procedure OKBtnClick(Sender: TObject);
  68.  
  69.     protected
  70.         { Protected declarations }
  71.         procedure Timeout; dynamic;
  72.  
  73.     public { Public declarations }
  74.         { Methods }
  75.         constructor Create(AOwner: TComponent); override;
  76.         destructor Destroy; override;
  77.         procedure Pick;
  78.         function Open: Boolean;
  79.         function Write(const sData: String): Boolean;
  80.         function Read: String;
  81.         procedure Flush;
  82.         procedure Close;
  83.  
  84.     published { Published declarations }
  85.         { timeout for waiting for a response, in mS }
  86.         property ResponseTime: LongInt read GetTimeout write SetTimeout;
  87.         { timeout event }
  88.         property OnTimeout: TNotifyEvent read pmOnTimeout write pmOnTimeout;
  89.         { flag set when a timeout occured }
  90.         property TimedOut: Boolean read boolExpired;
  91.         { baud rate }
  92.         property Baud: LongInt read lBaud write SetBaud;
  93.         { COM port }
  94.         property Port: Integer read nPort write SetPort;
  95.         { number of characters received }
  96.         property InCount: LongInt read GetInCount;
  97.         { string to signal end of input from external device }
  98.         property EndOfResponse: String read sInTerminator write sInTerminator;
  99.         { flag to enable showing error message boxes }
  100.         property ShowErrors: Boolean read boolShowErrors write boolShowErrors;
  101.         { flag indicating if port is open }
  102.         property Opened: Boolean read IsOpen;
  103.         { flag indication if port is blocked }
  104.         property InUse: Boolean read boolInUse;
  105. end;
  106.  
  107. procedure Register;
  108.  
  109. implementation
  110.  
  111. {$R *.DFM}
  112.  
  113. {
  114. Register the component with the Delphi IDE
  115. }
  116. procedure Register;
  117. begin
  118.     RegisterComponents('QC', [T_QCCom32]);
  119. end;
  120.  
  121. {
  122. Component constructor
  123. }
  124. constructor T_QCCom32.Create(AOwner: TComponent);
  125. begin
  126.     inherited Create(AOwner);
  127.     { set default property values }
  128.     hPort := INVALID_HANDLE_VALUE; { invalidate to start }
  129.     lBaud := 9600;
  130.     nPort := 1;
  131.     boolExpired := False;
  132.     boolShowErrors := True;
  133.     boolInUse := False;
  134.  
  135.     { create a timer for checking for timeout }
  136.     Timer := TTimer.Create(Self);
  137.     Timer.Enabled := False;
  138.     Timer.Interval := 1000; { 1 second }
  139.     Timer.OnTimer := TimesUp;
  140. end;
  141.  
  142. {
  143. Component destructor
  144. }
  145. destructor T_QCCom32.Destroy;
  146. begin
  147.     { deallocate the timer }
  148.     Timer.Free;
  149.     Timer := nil;
  150.     { close the com port (if open) }
  151.     Close;
  152.     inherited Destroy;    { destroy ancestor class }
  153. end;
  154.  
  155. {
  156. Set the Timeout property to the specified milliseconds
  157. }
  158. procedure T_QCCom32.SetTimeout(lTimeout: LongInt);
  159. begin
  160.     if Assigned(Timer) then Timer.Interval := lTimeout;
  161. end;
  162.  
  163. {
  164. Get the current timeout setting
  165. }
  166. function T_QCCom32.GetTimeout: LongInt;
  167. begin
  168.     Result := 0;
  169.     if Assigned(Timer) then Result := Timer.Interval;
  170. end;
  171.  
  172. {
  173. Return True if port is open
  174. }
  175. function T_QCCom32.IsOpen: Boolean;
  176. begin
  177.     //if hPort <> INVALID_HANDLE_VALUE then Result := True else Result := False;
  178.     Result := (hPort <> INVALID_HANDLE_VALUE);
  179. end;
  180.  
  181. {
  182. Set the baud rate property
  183. }
  184. procedure T_QCCom32.SetBaud(lBaudToSet: LongInt);
  185. begin
  186.     if lBaudToSet <> lBaud then
  187.     begin
  188.         lBaud := lBaudToSet;
  189.         { if port is open, then close it and then reopen it
  190.         to reset the baud rate }
  191.         if IsOpen then
  192.         begin
  193.             Close;
  194.             Open;
  195.         end;
  196.     end;
  197. end;
  198.  
  199. {
  200. Set the Port property
  201. }
  202. procedure T_QCCom32.SetPort(nPortToSet: Integer);
  203. begin
  204.     if nPortToSet <> nPort then
  205.     begin
  206.         nPort := nPortToSet;
  207.         { if port was open, then close and reopen it}
  208.         if hPort <> INVALID_HANDLE_VALUE then
  209.         begin
  210.             Close;
  211.             Open;
  212.         end;
  213.      end;
  214. end;
  215.  
  216. { Pops a dialog to pick the COM port number }
  217. procedure T_QCCom32.Pick;
  218. begin
  219.     formPickCom := TformPickCom.Create(Self); // create the box
  220.     formPickCom.radioCOM.ItemIndex := nPort - 1;
  221.     formPickCom.ShowModal; // show it
  222.     if formPickCom.ModalResult = mrOk then    SetPort(formPickCom.radioCOM.ItemIndex + 1);
  223.     formPickCom.Destroy; // throw it away
  224. end;
  225.  
  226. { Opens the COM port, returns True if ok }
  227. function T_QCCom32.Open: Boolean;
  228. var
  229.     sCom: String;
  230.     dcbPort: TDCB; {device control block }
  231.     boolAbort: Boolean;
  232.     sErrMsg: String;
  233. begin
  234.  
  235.     { init }
  236.     boolAbort := True;
  237.     //boolInUse := True;
  238.  
  239.     { close port if open already }
  240.     if hPort <> INVALID_HANDLE_VALUE then Close;
  241.  
  242.     { try to open the port }
  243.     repeat
  244.     begin
  245.         sCom := 'COM' + IntToStr(nPort);
  246.         hPort := CreateFile(PChar(sCom), GENERIC_READ or GENERIC_WRITE, 0, nil,
  247.                     OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, LongInt(0));
  248.  
  249.         if (hPort = INVALID_HANDLE_VALUE) and boolShowErrors then
  250.         begin
  251.             if MessageDlg('Error opening COM' + IntToStr(nPort) + ': ' + sErrMsg,
  252.                 mtWarning, [mbAbort, mbRetry], 0) = idAbort then
  253.                 boolAbort := True
  254.             else
  255.                 boolAbort := False;
  256.             end;
  257.         end;
  258.     until (hPort <> INVALID_HANDLE_VALUE) or (boolAbort = True);
  259.  
  260.     { set the baud rate and other parameters }
  261.     if (hPort <> INVALID_HANDLE_VALUE) then
  262.     begin
  263.         if GetCommState(hPort, dcbPort) then
  264.         begin
  265.             { fill in the fields of the structure }
  266.             dcbPort.BaudRate := lBaud;
  267.             dcbPort.ByteSize := 8;
  268.             dcbPort.Parity := NOPARITY;
  269.             dcbPort.StopBits := ONESTOPBIT;
  270.             dcbPort.Flags := 0;
  271.             { flag bit fields:
  272.             dcb_Binary, dcb_Parity, dcb_OutxCtsFlow, dcb_fOutxDsrFlow,
  273.             dcb_fOutX, dcb_fInX, dcb_DtrFlow, dcb_RtsFlow
  274.             }
  275.             SetCommState(hPort, dcbPort);
  276.         end;
  277.     end;
  278.  
  279.     { return True if handle is valid }
  280.     Result := (hPort <> INVALID_HANDLE_VALUE);
  281.     //boolInUse := False;
  282.  
  283. end;
  284.  
  285. { Close the COM port }
  286. procedure T_QCCom32.Close;
  287. begin
  288.     if hPort <> INVALID_HANDLE_VALUE then CloseHandle(hPort);
  289.     hPort := INVALID_HANDLE_VALUE;
  290.     //boolInUse := False;
  291. end;
  292.  
  293. { Write a string out the COM port, return true if all chars written }
  294. function T_QCCom32.Write(const sData: String): Boolean;
  295. var
  296.     dwCharsWritten: DWord;
  297.  
  298. begin
  299.     { init }
  300.     //boolInUse := True;
  301.     dwCharsWritten := 0;
  302.     Result := False; { default to error return }
  303.  
  304.     if hPort <> INVALID_HANDLE_VALUE then
  305.     begin
  306.         WriteFile(hPort, PChar(sData)^, Length(sData), dwCharsWritten, nil);
  307.         if dwCharsWritten = Length(sData) then Result := True;
  308.     end;
  309.  
  310.     //boolInUse := False;
  311.  
  312. end;
  313.  
  314. { Return the number of bytes waiting in the queue }
  315. function T_QCCom32.GetInCount: LongInt;
  316. var
  317.     statPort: TCOMSTAT;
  318.     dwErrorCode: DWord;
  319. begin
  320.     Result := 0;
  321.     if hPort <> INVALID_HANDLE_VALUE then
  322.      begin
  323.         ClearCommError(hPort, dwErrorCode, @statPort);
  324.         Result := statPort.cbInQue;
  325.      end;
  326. end;
  327.  
  328. {
  329. Reads a string from the port, puts it into pchBuffer, returns the
  330. number of characters read
  331. }
  332. function T_QCCom32.Read: String;
  333. const
  334.     BUF_LEN = 1024;
  335. var
  336.     cbCharsAvailable, cbCharsRead: DWord;
  337.     boolExit: Boolean;
  338.     sBuffer: String;
  339.  
  340. begin
  341.     { init }
  342.     SetLength(sBuffer, BUF_LEN);
  343.     Result := '';
  344.  
  345.    { check boolInUse in case of rentrancy }
  346.     if boolInUse then
  347.     begin
  348.         if boolShowErrors then ShowMessage('Port is in use -- operation aborted');
  349.         Exit;
  350.     end
  351.     else
  352.         boolInUse := True;
  353.  
  354.         if hPort <> INVALID_HANDLE_VALUE then
  355.         begin
  356.             { if no terminator is defined, simply read any available data and return }
  357.             if Length(sInTerminator) = 0 then
  358.             begin
  359.                 cbCharsAvailable := GetInCount;
  360.                 if cbCharsAvailable > 0 then
  361.                 begin
  362.                     SetLength(Result, cbCharsAvailable + 1); { allocate space }
  363.                     ReadFile(hPort, PChar(Result)^, cbCharsAvailable, cbCharsRead, nil);
  364.                     SetLength(Result, StrLen(PChar(Result))); { adjust length }
  365.                 end;
  366.             end
  367.         else { a terminator is defined, so read port until terminator found or timed out }
  368.         begin
  369.             boolExit := False;
  370.  
  371.             repeat
  372.                 boolExpired := False;
  373.                 Timer.Enabled := True;    { start the timeout timer }
  374.  
  375.                 { loop until timeout or terminator recieved }
  376.                 repeat
  377.                     cbCharsAvailable := GetInCount;
  378.                     if cbCharsAvailable > 0 then
  379.                     begin
  380.                         if cbCharsAvailable >= Length(sBuffer) then
  381.                             SetLength(sBuffer, cbCharsAvailable + 1); { allocate space }
  382.  
  383.                         ReadFile(hPort, PChar(sBuffer)^, cbCharsAvailable, cbCharsRead, nil);
  384.  
  385.                         { append chars read to end of result buffer }
  386.                         Result := Result + Copy(sBuffer, 0, StrLen(PChar(sBuffer)));
  387.                     end;
  388.  
  389.                     if Pos(sInTerminator, Result) <> 0 then
  390.                     begin
  391.                         boolExit := True;
  392.                     end
  393.                     else
  394.                     begin
  395.                         { give timer message a chance to occur - watch out for rentrancy!!}
  396.                         Application.ProcessMessages;
  397.                     end;
  398.  
  399.                 until (boolExpired) or (boolExit);
  400.  
  401.                 Timer.Enabled := False; { stop the timer }
  402.  
  403.                 { If timed out, then give user a chance to retry }
  404.                 if boolExpired and boolShowErrors then
  405.                 begin
  406.                     if MessageDlg('Timeout waiting for response.', mtWarning,
  407.                     [mbAbort, mbRetry], 0) = idAbort then boolExit := True;
  408.                 end;
  409.  
  410.             until boolExit = True;
  411.         end;
  412.     end;
  413.     boolInUse := False;
  414. end;
  415.  
  416. {
  417. Timer procedure - signals a timeout
  418. }
  419. procedure T_QCCom32.TimesUp(Sender: TObject);
  420. begin
  421.     boolExpired := True;
  422.     Timer.Enabled := False;
  423.     Timeout; // execute user code
  424. end;
  425.  
  426. {
  427. Timeout event - user assigned code
  428. }
  429. procedure T_QCCom32.Timeout;
  430. begin
  431.     { if user has assigned code to the timeout event, then call it }
  432.     if Assigned(pmOnTimeout) then pmOnTimeout(self);
  433. end;
  434.  
  435. {
  436. Flush the port by reading any characters in the queue
  437. }
  438. procedure T_QCCom32.Flush;
  439. begin
  440.     if hPort <> INVALID_HANDLE_VALUE then
  441.     begin
  442.         PurgeComm(hPort, PURGE_TXABORT or PURGE_RXABORT or PURGE_TXCLEAR or PURGE_RXCLEAR);
  443.     end;
  444. end;
  445.  
  446.  
  447.  
  448.  
  449. //
  450. // Dialog box functions
  451. //
  452.  
  453. {
  454. procedure T_QCCom32.FormActivate(Sender: TObject);
  455. begin
  456.     formPickCom.radioCOM.ItemIndex := nPort - 1;
  457. end;
  458.  
  459. procedure T_QCCom32.OKBtnClick(Sender: TObject);
  460. begin
  461.     SetPort(formPickCom.radioCOM.ItemIndex + 1);
  462. end;
  463. }
  464. end.
  465.